package ru.mirent;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatchers;
import org.mockito.InOrder;
import org.mockito.exceptions.verification.NoInteractionsWanted;

import java.util.List;
import java.util.Map;

import static org.mockito.Mockito.*;

/**
 * Проверка количества и порядка вызовов методов
 */
public class MockitoVerifyTest {

    @Test
    void verifyExamplesTest() {
        List<String> mockList = mock(List.class);

        mockList.add("Test Text");
        mockList.size();

        // Проверка вызова тестируемого метода с указанным значением параметра
        verify(mockList).add("Test Text");

        // Проверка вызова тестируемого метода с указанным типом данных
        verify(mockList).add(anyString());
        verify(mockList).add(any(String.class));
        verify(mockList).add(ArgumentMatchers.any(String.class));

        // Типы проверок указанного количества для метода:
        // Стандартная проверка с указанием количества вызова
        verify(mockList, times(1)).size();
        // Должен быть вызван минимум один раз
        verify(mockList, atLeastOnce()).size();
        // Должен быть вызван не более указанного количества раз
        verify(mockList, atMost(2)).size();
        // Должен быть вызван хотя бы указанное количество раз
        verify(mockList, atLeast(1)).size();
        // Метод не должен быть вызван
        verify(mockList, never()).clear();
        // Вызов проверки после указанного ожидания (в мс.)
        verify(mockList, timeout(100)).size();
    }

    /**
     * Провекрка вызова только одного метода. Пример вынесен в отдельный класс для демонстрации работы, т.к.
     * в методе {@link #verifyExamplesTest()} не удавалось продемонстрировать его работу.
     */
    @Test
    void onlyExampleTest() {
        List<String> mockList = mock(List.class);
//        mockList.add("Test Text"); // При вызове метода, отличного от проверяемого будет показана ошибка
        mockList.size();

        // Проверить что вызван только указанный метод
        verify(mockList, only()).size();
    }

    /**
     * Проверить что все взаимодействия с замоканным объектом были проверены.
     */
    @Test
    void verifyNoMoreInteractionsFailTest() {
        List<String> mockList = mock(List.class);
        mockList.isEmpty();

        // Вызов тестируемого метода
        // Метод isEmpty() был вызван в mock объекте, но вызов не был проверен - будет показана ошибка
        NoInteractionsWanted noInteractionsWanted = Assertions.assertThrows(NoInteractionsWanted.class, () -> verifyNoMoreInteractions(mockList));
        // Проверка соответствия сообщения об ошибке
        Assertions.assertTrue(noInteractionsWanted.getLocalizedMessage().contains("No interactions wanted here"));
    }

    /**
     * Проверка порядка вызова методов разных объектов
     */
    @Test
    void inOrderTest() {
        // Объявление заглушек классов
        List<String> mockList = mock(List.class);
        Map<String, String> mockMap = mock(Map.class);

        // Вызов проверяемых методов
        mockList.add("Test Text");
        mockList.size();
        mockList.isEmpty();
        mockMap.isEmpty();

        // Проверка порядка вызовов
        InOrder inOrder = inOrder(mockList, mockMap);
        inOrder.verify(mockList).add(anyString());
        inOrder.verify(mockList).size();
        // Можно проверять порядок только необходимых вызовов методов, а ненужные пропускать
//        inOrder.verify(mockList).isEmpty();
        inOrder.verify(mockMap).isEmpty();
    }
}
